package cn.xorange.commons.security.utils;


import cn.xorange.commons.security.configuration.SecurityProperties;
import cn.xorange.commons.security.constants.CacheConstants;
import cn.xorange.commons.security.constants.HeaderConstants;
import cn.xorange.commons.security.constants.SecurityConstants;
import cn.xorange.commons.security.entity.ClaimsInfo;
import cn.xorange.commons.security.entity.SecurityUser;
import cn.xorange.commons.security.exception.SecurityErrorCode;
import cn.xorange.commons.security.exception.SecurityException;
import cn.xorange.commons.utils.http.ServletUtils;
import cn.xorange.commons.utils.lang.StringUtils;
import cn.xorange.commons.utils.spring.SpringUtils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * @author yangjian
 */
@Slf4j
public class JwtUtils {
    private JwtUtils(){}

    /**
     * token默认的长度
     */
    private static final Integer DEFAULT_TOKEN_SIZE = 3;

    /** 兜底有效期：24小时 */
    private static final Long TOKEN_EXPIRATION = 24 * 60L;

    /**
     * 创建jwtToken
     * jwtToken 时效性由 缓存控制
     */
    public static String createJwtToken(String sysType, String loginType, String source, String tokenId, Long expireTimes){
        if(Objects.isNull(expireTimes)){
            expireTimes = TOKEN_EXPIRATION;
        }
        String jwt = JWT.create()
                // 签发对象
                .withAudience(sysType)
                // 通过cache来控制
//                // 签发时间
                .withIssuedAt(new Date())
//                // 过期时间
                .withExpiresAt(new Date(System.currentTimeMillis() + expireTimes * 60 * 1000) )
                .withClaim(SecurityConstants.TOKEN_ID, tokenId)
                .withClaim(SecurityConstants.LOGIN_TYPE, loginType)
                .withClaim(HeaderConstants.X_SOURCE, source)
                .sign(Algorithm.HMAC256(SpringUtils.getBean(SecurityProperties.class).getSalt()));
        // 混淆token
        return mixToken(jwt);
    }

    /**
     * 根据request请求 获取 JwtToken
     * 1、反混淆
     * 2、token正确性验证
     */
    public static String getJwtToken(HttpServletRequest request, HttpServletResponse response){
        String token = request.getHeader(SecurityConstants.HEADER_AUTHORIZATION);
        if (Objects.nonNull(token)){
            if (token.startsWith(CacheConstants.TOKEN_PREFIX)) {
                token = token.replace(CacheConstants.TOKEN_PREFIX, "");
                if(!token.isEmpty()&& !"undefined".equals(token)){
                    return JwtUtils.unMixToken(token);
                }
            }
        }
        return null;
    }

    public static void validateToken(HttpServletResponse response, String unMixJwtToken){
        try{
            // 验证JwtToken
            JwtUtils.validateToken(unMixJwtToken);
        }catch (Exception e){
            log.debug("jwtToken反混淆校验失败");
            SecurityErrorCode errorCode = SecurityErrorCode.AUTH_TOKEN_UNAUTHORIZED;
            ServletUtils.responseOut(response, errorCode.getStatus(), errorCode.getCode(), errorCode.getMsg(), null);
        }
    }

    /**
     * 获取签发对象
     */
    public static String getAudience(String jwtToken) {
        try {
            return JWT.decode(jwtToken).getAudience().get(0);
        } catch (JWTDecodeException j) {
            /* 这里是token解析失败 */
            throw SecurityException.authUnauthorized();
        }
    }

    public static ClaimsInfo getClaims(String jwtToken){
        DecodedJWT decode = JWT.decode(jwtToken);
        String audience = decode.getAudience().get(0);
        String loginType = decode.getClaims().get(SecurityConstants.LOGIN_TYPE).asString();
        String source = decode.getClaims().get(HeaderConstants.X_SOURCE).asString();
        String tokenId= decode.getClaims().get(SecurityConstants.TOKEN_ID).asString();
        return new ClaimsInfo(audience,loginType,source,tokenId);
    }

    /**
     * 编码位移
     *
     * @param payload payload编码
     * @param index   位移处
     */
    private static String reversePayload(String payload, Integer index) {
        return payload.substring(index) + payload.substring(0, index);
    }

    /**
     * 混淆token
     * 对于加密的思路还有位移、字符替换等~
     *
     * @param jwtToken 混淆前的jwtToken
     */
    private static String mixToken(String jwtToken) {
        //分割token
        String[] split = jwtToken.split("\\.");
        //如果token不符合规范
        if (split.length != DEFAULT_TOKEN_SIZE) {
            throw new JWTDecodeException("签名不正确");
        }
        //取出payload
        String payload = split[1];
        //获取长度
        int length = payload.length() / 2;
        //指定截取点
        int index = payload.length() % 2 != 0 ? length + 1 : length;
        split[1] = reversePayload(payload, index);
        //混淆处理后的token
        StringBuilder sb = new StringBuilder();
        /**
         sb.append(split[0].substring(0,5));
         sb.append(".");
         sb.append(split[0].substring(5));
         sb.append(".");
         sb.append(claims.substring(0,10));
         sb.append(".");
         sb.append(claims.substring(10));
         sb.append(".");
         sb.append(split[2].substring(0,5));
         sb.append(".");
         sb.append(split[2].substring(5));
         */
        sb.append(split[0], 0, 5);   // 0 0 (0-5)
        sb.append(split[2].substring(5));     // 0 2 (5)
        sb.append(".");
        sb.append(split[0].substring(5));     // 1 0 (5)
        sb.append(split[1], 0, 5);     // 1 1 (0-5)
        sb.append(".");
        sb.append(split[2], 0, 5);   // 2 2
        sb.append(split[1].substring(5));       // 2 1
        return sb.toString();
    }


    /**
     * 反混淆jtwToken
     *
     * @param mixToken 混淆后的jwtToken
     */
    private static String unMixToken(String mixToken) {
        String[] split = mixToken.split("\\.");
        //如果token不符合规范
        if (split.length != DEFAULT_TOKEN_SIZE) {
            throw SecurityException.authUnauthorized();
        }
        String payload = StringUtils.format("{}{}",split[1].substring(split[1].length()-5),split[2].substring(5));
        //还原token
        StringBuilder sb = new StringBuilder();
        sb.append(split[0].substring(0,5));
        sb.append(split[1].substring(0,split[1].length()-5));
        sb.append(".");
        // 还原混淆的payload
        sb.append(reversePayload(payload, payload.length() / 2));
        /**
        sb.append(split[1].substring(split[1].length()-5,split[1].length()));
        sb.append(split[2].substring(5));
         */
        sb.append(".");
        sb.append(split[2].substring(0,5));
        sb.append(split[0].substring(5));
        return sb.toString();
    }

    /**
     * 判断前台token 有效性
     * @param jwtToken
     * @return
     */
    private static void validateToken(String jwtToken) {
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SpringUtils.getBean(SecurityProperties.class).getSalt())).build();
            verifier.verify(jwtToken);
        }catch (Exception e){
            throw SecurityException.authUnauthorized();
        }
    }







}
